// backend-example.js
// 这是一个Node.js + Express的后端接口示例
// 用于展示生产环境中如何正确处理微信支付

const express = require('express')
const crypto = require('crypto')
const axios = require('axios')
const xml2js = require('xml2js')

const app = express()
app.use(express.json())

// 微信支付配置（应该从环境变量或配置文件中读取）
const WX_PAY_CONFIG = {
  appid: process.env.WX_APPID || 'wx8888888888888888',
  mch_id: process.env.WX_MCH_ID || '1900000109',
  key: process.env.WX_PAY_KEY || '192006250b4c09247ec02edce69f6a2d',
  notify_url: process.env.WX_NOTIFY_URL || 'https://your-domain.com/api/wechat/notify',
  unified_order_url: 'https://api.mch.weixin.qq.com/pay/unifiedorder'
}

// 生成随机字符串
function generateNonceStr() {
  return crypto.randomBytes(16).toString('hex')
}

// 获取客户端真实IP
function getClientIP(req) {
  return req.headers['x-forwarded-for'] || 
         req.connection.remoteAddress || 
         req.socket.remoteAddress ||
         (req.connection.socket ? req.connection.socket.remoteAddress : null) ||
         '127.0.0.1'
}

// 计算微信支付签名
function calculateSign(params, key) {
  // 1. 参数排序（排除sign字段）
  const keys = Object.keys(params)
    .filter(k => params[k] !== undefined && params[k] !== '' && k !== 'sign')
    .sort()

  // 2. 拼接字符串
  let stringA = ''
  keys.forEach((key, index) => {
    if (index > 0) stringA += '&'
    stringA += `${key}=${params[key]}`
  })

  // 3. 拼接API密钥
  const stringSignTemp = `${stringA}&key=${key}`

  // 4. MD5加密并转大写
  return crypto.createHash('md5').update(stringSignTemp, 'utf8').digest('hex').toUpperCase()
}

// 对象转XML
function buildXML(obj) {
  let xml = '<xml>'
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      xml += `<${key}><![CDATA[${obj[key]}]]></${key}>`
    }
  }
  xml += '</xml>'
  return xml
}

// XML转对象
async function parseXML(xml) {
  const parser = new xml2js.Parser({ explicitArray: false, ignoreAttrs: true })
  return new Promise((resolve, reject) => {
    parser.parseString(xml, (err, result) => {
      if (err) reject(err)
      else resolve(result.xml)
    })
  })
}

// 验证签名
function verifySign(params, key) {
  const sign = params.sign
  delete params.sign
  const calculatedSign = calculateSign(params, key)
  return sign === calculatedSign
}

/**
 * 统一下单接口
 * POST /api/wechat/pay
 */
app.post('/api/wechat/pay', async (req, res) => {
  try {
    const { body, total_fee, out_trade_no, openid } = req.body

    // 参数验证
    if (!body || !total_fee || !out_trade_no || !openid) {
      return res.status(400).json({
        return_code: 'FAIL',
        return_msg: '参数不完整'
      })
    }

    // 构建统一下单参数
    const unifiedOrderParams = {
      appid: WX_PAY_CONFIG.appid,
      mch_id: WX_PAY_CONFIG.mch_id,
      nonce_str: generateNonceStr(),
      body: body,
      out_trade_no: out_trade_no,
      total_fee: parseInt(total_fee),
      spbill_create_ip: getClientIP(req),
      notify_url: WX_PAY_CONFIG.notify_url,
      trade_type: 'JSAPI',
      openid: openid
    }

    // 计算签名
    const sign = calculateSign(unifiedOrderParams, WX_PAY_CONFIG.key)
    unifiedOrderParams.sign = sign

    // 转换为XML
    const xmlData = buildXML(unifiedOrderParams)

    console.log('统一下单请求参数:', unifiedOrderParams)
    console.log('签名字符串:', xmlData)

    // 调用微信统一下单接口
    const response = await axios.post(WX_PAY_CONFIG.unified_order_url, xmlData, {
      headers: {
        'Content-Type': 'application/xml'
      }
    })

    // 解析返回结果
    const result = await parseXML(response.data)
    console.log('微信返回结果:', result)

    if (result.return_code === 'SUCCESS') {
      if (result.result_code === 'SUCCESS') {
        // 统一下单成功，准备支付参数
        const timeStamp = Math.floor(Date.now() / 1000).toString()
        const nonceStr = generateNonceStr()
        const packageStr = `prepay_id=${result.prepay_id}`
        
        const paySignParams = {
          appId: WX_PAY_CONFIG.appid,
          timeStamp: timeStamp,
          nonceStr: nonceStr,
          package: packageStr,
          signType: 'MD5'
        }

        // 计算支付签名
        const paySign = calculateSign(paySignParams, WX_PAY_CONFIG.key)

        // 返回支付参数给前端
        res.json({
          return_code: 'SUCCESS',
          result_code: 'SUCCESS',
          ...paySignParams,
          paySign: paySign,
          prepay_id: result.prepay_id
        })

        // 这里可以保存订单信息到数据库
        // await saveOrder({ out_trade_no, total_fee, status: 'pending' })

      } else {
        res.json({
          return_code: 'FAIL',
          return_msg: result.err_code_des || '下单失败'
        })
      }
    } else {
      res.json({
        return_code: 'FAIL',
        return_msg: result.return_msg || '调用微信接口失败'
      })
    }

  } catch (error) {
    console.error('统一下单失败:', error)
    res.status(500).json({
      return_code: 'FAIL',
      return_msg: '服务器内部错误'
    })
  }
})

/**
 * 微信支付回调通知接口
 * POST /api/wechat/notify
 */
app.post('/api/wechat/notify', async (req, res) => {
  try {
    let body = ''
    req.on('data', chunk => {
      body += chunk.toString()
    })

    req.on('end', async () => {
      console.log('微信支付回调通知:', body)

      try {
        // 解析XML数据
        const result = await parseXML(body)

        // 验证签名
        if (!verifySign({ ...result }, WX_PAY_CONFIG.key)) {
          console.error('签名验证失败')
          return res.end('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>')
        }

        // 处理支付结果
        if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
          const { out_trade_no, transaction_id, total_fee } = result
          
          console.log('支付成功:', {
            out_trade_no,
            transaction_id,
            total_fee
          })

          // 这里处理支付成功的业务逻辑
          // 1. 更新订单状态
          // 2. 发送通知
          // 3. 处理商品发货等
          // await updateOrderStatus(out_trade_no, 'paid', transaction_id)

          // 返回成功响应
          res.end('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>')
        } else {
          console.log('支付失败:', result)
          // 处理支付失败逻辑
          // await updateOrderStatus(result.out_trade_no, 'failed')
          
          res.end('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>')
        }

      } catch (error) {
        console.error('处理回调通知失败:', error)
        res.end('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[处理失败]]></return_msg></xml>')
      }
    })

  } catch (error) {
    console.error('接收回调通知失败:', error)
    res.status(500).end()
  }
})

/**
 * 查询订单状态接口
 * GET /api/wechat/order/:out_trade_no
 */
app.get('/api/wechat/order/:out_trade_no', async (req, res) => {
  try {
    const { out_trade_no } = req.params

    const queryParams = {
      appid: WX_PAY_CONFIG.appid,
      mch_id: WX_PAY_CONFIG.mch_id,
      out_trade_no: out_trade_no,
      nonce_str: generateNonceStr()
    }

    const sign = calculateSign(queryParams, WX_PAY_CONFIG.key)
    queryParams.sign = sign

    const xmlData = buildXML(queryParams)

    const response = await axios.post('https://api.mch.weixin.qq.com/pay/orderquery', xmlData, {
      headers: {
        'Content-Type': 'application/xml'
      }
    })

    const result = await parseXML(response.data)
    res.json(result)

  } catch (error) {
    console.error('查询订单失败:', error)
    res.status(500).json({
      return_code: 'FAIL',
      return_msg: '查询订单失败'
    })
  }
})

// 启动服务器
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`微信支付后端服务启动成功，端口: ${PORT}`)
  console.log('API接口:')
  console.log(`  POST /api/wechat/pay - 统一下单`)
  console.log(`  POST /api/wechat/notify - 支付回调`)
  console.log(`  GET /api/wechat/order/:out_trade_no - 查询订单`)
})

module.exports = app 